/*
 * CAnt.cpp
 *
 *  Created on: 14 Oct 2012
 *      Author: Mario Kleinsasser
 *		Time:	17:23:45
 */

#include <iostream>
#include <list>
#include <map>
#include <string>
#include <string.h>
#include <ctime>
#include <stdlib.h>
#include <typeinfo>

#include "CFood.h"
#include "CArea.h"
#include "CEnvironment.h"
#include "CAnt.h"
#include "CAnthill.h"

using namespace std;

/* struct needed to randomly choose direction */
struct direction {
	string dir;
	int dirint;
	CArea* to;
	int ph;

	int M;
	int N;
};

bool CAnt::checkForAreaType(CArea *area) {

	list<CItem*> itemList;
	list<CItem*>::iterator it;
	itemList = area->getItemList();

	/* check if the field near to us is a food field */

	for (it = itemList.begin(); it != itemList.end(); it++) {
		CFood* food = dynamic_cast<CFood*>(*it);
		/* only take food if we have no food */
		if ((food != NULL) && (this->foundFood == 0)) {
			/* cout << "Food found \n"; */
			this->foundFood = 20;
			this->foodlivetime += 20;
			return true;
		}
	}

	/* check if the field near to us is an AntHill field */
	for (it = itemList.begin(); it != itemList.end(); it++) {
		CAnthill* hill = dynamic_cast<CAnthill*>(*it);
		/* only take food if we have no food */
		if ((hill != NULL) && (this->foundFood > 0)) {
			hill->setFood(hill->getFood() + this->foodlivetime);

			this->foodlivetime = -1;

			list<CItem*> anthillAntList = hill->getAntList();
			anthillAntList.push_front(this);
			hill->setAntList(anthillAntList);

			area->raisePheromone(20);

			return true;
		}
	}

	return false;

}

void CAnt::calculateMove(CEnvironment &environment) {

	CArea* area = environment.getPlayground()->getArea(this->getM(), this->getN());

	/* get the list of the current area by value */
	list<CItem*> itemList;
	//itemList = environment.getPlayground()->getArea(this->getM(), this->getN())->getItemList();
	itemList = area->getItemList();

	/* check where we can move to move */

	map<int, direction> possibleMoves;
	map<int, direction>::iterator element;

	/* helper variable to make map easier to handle */
	int pos = 1;

	if (area->getWest() != NULL) {
		direction tmp;
		tmp.dir = "west";
		tmp.dirint = 1;
		tmp.to = area->getWest();
		tmp.ph = tmp.to->getPheromone();
		tmp.M = this->M;
		tmp.N = (this->N) - 1;
		/* dont move ON the food and dont go back on the field we come from */
		if (!checkForAreaType(tmp.to) && !(tmp.M == this->lastM && tmp.N == this->lastN)) {
			possibleMoves[1] = tmp;
			pos++; /* check if the filed next to us is food */
		}

	}
	if (area->getEast() != NULL) {
		direction tmp;
		tmp.dir = "east";
		tmp.dirint = 2;
		tmp.to = area->getEast();
		tmp.ph = tmp.to->getPheromone();
		tmp.M = this->M;
		tmp.N = (this->N) + 1;
		/* dont move ON the food and dont go back on the field we come from */
		if (!checkForAreaType(tmp.to) && !(tmp.M == this->lastM && tmp.N == this->lastN)) {
			possibleMoves[2] = tmp;
			pos++; /* check if the filed next to us is food */
		}
	}
	if (area->getNorth() != NULL) {
		direction tmp;
		tmp.dir = "north";
		tmp.dirint = 3;
		tmp.to = area->getNorth();
		tmp.ph = tmp.to->getPheromone();
		tmp.M = (this->M) - 1;
		tmp.N = (this->N);
		/* dont move ON the food and dont go back on the field we come from */
		if (!checkForAreaType(tmp.to) && !(tmp.M == this->lastM && tmp.N == this->lastN)) {
			possibleMoves[3] = tmp;
			pos++; /* check if the filed next to us is food */
		}
	}
	if (area->getSouth() != NULL) {
		direction tmp;
		tmp.dir = "south";
		tmp.dirint = 4;
		tmp.to = area->getSouth();
		tmp.ph = tmp.to->getPheromone();
		tmp.M = (this->M) + 1;
		tmp.N = (this->N);
		/* dont move ON the food and dont go back on the field we come from */
		if (!checkForAreaType(tmp.to) && !(tmp.M == this->lastM && tmp.N == this->lastN)) {
			possibleMoves[4] = tmp;
			pos++; /* check if the filed next to us is food */
		}
	}

	/*
	 * calculate overall probability
	 * if a field has no pheromone on it add temporaly 5 fake pheromones
	 * if all fields around are empty, all will have a possibility of 5
	 * if to fields have pheromone there will be a chance that the ant goes on the empty one
	 */
	int probability = 0;
	/* count the sum of phereomone around the ant*/
	for (element = possibleMoves.begin(); element != possibleMoves.end(); element++) {
		if (element->second.ph == 0) {
			probability += 5;
		} else {
			probability += element->second.ph;
		}
	}

	/* probability array*/
	int array[probability];

	int posarray = 0;
	/* write the weight of the pheromone to map */
	for (element = possibleMoves.begin(); element != possibleMoves.end(); element++) {
		int tmp = element->second.ph;
		if (tmp == 0) {
			tmp = 5;
		}

		if ((probability != 0) && (tmp != 0)) {
			for (int i = 0; i <= tmp; i++) {
				array[posarray] = element->second.dirint;
				posarray++;
			}
		}
	}

	posarray--;

	/* calculate where to move */
	int cnt = possibleMoves.size();
	int selector = 0;
	int newpos = (rand() % posarray) + 1;
	selector = array[newpos];

	/*
	 * if the livetime goes down, we switch back to random
	 * this will suppress infinite circle loops of ants
	 */

	if (foodlivetime <= 10) {
		selector = (rand() % cnt) + 1;
	}

	/* get the equal map via selector, get the value from the map */
	element = possibleMoves.find(selector);
	CArea* moveTo = element->second.to;

	/*
	 * if are out of livetime remove from list and return
	 * in other words the ant died :-)
	 */
	if (foodlivetime == 0) {
		itemList.remove(this);
		area->setItemList(itemList);
		OriginAnthill->setAntdiecount(OriginAnthill->getAntdiecount() + 1);
		return;
	}

	if (foodlivetime == -1) {
		itemList.remove(this);
		area->setItemList(itemList);
		return;
	}


	/* if we can move remove us from the existing list*/
	itemList.remove(this);
	area->setItemList(itemList);

	/* dont forget to set the new position into ant */

	/*
	 *  Only debug!
	 *  cout << "------- \n";
	 *  cout << this->M << " " << this->N << "\n";
	 */

	/* store old position , DANGER DONT SAVE TO EARLY*/
	this->lastM = this->M;
	this->lastN = this->N;

	/* store new position */
	this->M = element->second.M;
	this->N = element->second.N;

	/*
	 * Do the move and if we are on the way back home
	 * set pheromone
	 */
	itemList = moveTo->getItemList();
	itemList.push_back(this);
	moveTo->setItemList(itemList);

	/*
	 * on the way back home set pheromone
	 */
	if (this->foundFood > 0) {
		area->raisePheromone(20);
	}

	/*
	 * reduce the lifetime
	 */
	// cout << foodlivetime << "\n";
	this->foodlivetime -= 1;

}

int CAnt::act() {

	/* move ! */

	this->classType = "ant";

	CEnvironment &environment = CEnvironment::getInstance();

	if (this->turncnt >= environment.getTurncnt()) {
		/* We have already been moved in this turn ! */
		return 0;
	}

	calculateMove(environment);

	/*
	 * set turncnt to current turn count
	 * the turn count gets raised after all acts
	 * so we come in turn next round but only once per round
	 */

	this->turncnt = environment.getTurncnt();

	//print();

	return 0;
}

void CAnt::print() {
	cout << "+";
}

CAnt::CAnt() {
	this->turncnt = 0;
	this->foundFood = 0;
	this->foodlivetime = 20;
}

CAnt::~CAnt() {
	// TODO Auto-generated destructor stub
}

